// ==================================================================================
// Shared Genomics Project MPI Codebase
// Version 1.0 30/04/2010
//
// (c) 2010 University of Manchester all rights reserved
//
// This file is distributed under the GNU General Public License, Version 2.  
// Please see the file COPYING.txt for more details
// ==================================================================================

/*!
\file
\brief Implementation of SNP 2-way interaction (Small Structure) 
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "copyfile.h"
#include "pmaths.h"
#include "small_int2.h"
#include "suffixes.h"

void small_int2_init(struct small_int2 *i) {
	if (i != NULL) {
		i->p1 = i->p2 = -1;
		i->z = i->coeff = 0;
	}
}

struct small_int2* small_int2_create_array(int size, BOOL initialise) {
	struct small_int2 *data = NULL;
	
	if (size > 0) {
		data = (struct small_int2*) malloc(sizeof(struct small_int2) * size);
		if (data != NULL && initialise) {
			int i;
			for (i = 0; i < size; i++) small_int2_init(&data[i]);
		}
	}

	return data;
}

void small_int2_echo(struct small_int2 *i2) {
	if (i2 != NULL) fprintf(stdout, "p1=%i, p2=%i, z=%f\n", i2->p1, i2->p2, i2->z);
}

int small_int2_write_and_copy(int numprocs, int rank, int nSrc, struct small_int2 *src, struct selected_options *ops) {
	BOOL jobDone = FALSE;
	char localFilepath[FILE_PATH_LENGTH], remoteFilepath[FILE_PATH_LENGTH];
	FILE *out = NULL;

	if (numprocs <= 0 || nSrc <= 0 || src == NULL || ops == NULL || rank < 0) return 0;
	else if (strlen(ops->szRunId) == 0 || strlen(ops->ldir) == 0 || strlen(ops->rdir) == 0) return 0;

	sprintf(localFilepath, "%s%s_%i%s", ops->ldir, ops->szRunId, rank, SUFFIX_RAW_EPI_FILE);
	sprintf(remoteFilepath, "%s%s_%i%s", ops->rdir, ops->szRunId, rank, SUFFIX_RAW_EPI_FILE);
	
	out = fopen(localFilepath, "wb");
	if (out != NULL) {
		BOOL wroteLocalFile = FALSE;
		if(fwrite(&numprocs, sizeof(int), 1, out) == 1) {
			if(fwrite(&nSrc, sizeof(int), 1, out) == 1) {
				if(fwrite(src, sizeof(struct small_int2), nSrc, out) == nSrc) wroteLocalFile = TRUE;
			}
		}
		fclose(out);

		if (wroteLocalFile) {
			if (copyAndDeleteFile(localFilepath, remoteFilepath)) jobDone = TRUE;
		}
	}

	if (jobDone) return 1;
	else return 0;
}

struct small_int2* small_int2_load(int nSNPs, struct selected_options *ops, int *size, int *nCores) {
	const int max_attempts = 10;
	struct small_int2* data = NULL, *pData = NULL;
	char remoteFilepath[FILE_PATH_LENGTH];
	int numprocs = -1, i, elements_read = 0;

	if (nSNPs <= 0 || ops == NULL || size == NULL || nCores  == NULL) return NULL;
	else if (strlen(ops->szRunId) == 0 || strlen(ops->rdir) == 0) return NULL;

	*nCores = *size = 0; // Initialise output count to zero.

	// Read the numprocs from the MPI output files.
	for (i = 0; i < max_attempts; i++) {
		FILE *in = NULL;
		sprintf(remoteFilepath, "%s%s_%i%s", ops->rdir, ops->szRunId, i, SUFFIX_RAW_EPI_FILE);
		in = fopen(remoteFilepath, "rb");
		if (in != NULL) {
			if (fread(&numprocs, sizeof(int), 1, in) != 1) numprocs = -1;
			fclose(in);
			break;
		}
	}
	if (numprocs == -1 || numprocs < 0) return NULL;
	
	// Allocate the data-set buffer and read each fragment in turn.
	data = small_int2_create_array((int) getPairwiseTests(nSNPs), TRUE);
	if (data != NULL) {
		pData = data;
		for (i = 0; i < numprocs; i++) {
			FILE *in = NULL;
			sprintf(remoteFilepath, "%s%s_%i%s", ops->rdir, ops->szRunId, i, SUFFIX_RAW_EPI_FILE);
			in = fopen(remoteFilepath, "rb");
			if (in != NULL) {
				int _numprocs = 0, nElements = 0;
				if (fread(&_numprocs, sizeof(int), 1, in) == 1) {
					if(fread(&nElements, sizeof(int), 1, in) == 1) {
						if (nElements > 0) {
							if (fread(pData, sizeof(struct small_int2), nElements, in) == nElements) {
								pData += nElements;
								elements_read += nElements;
							}
						}
					}
				}
				fclose(in);
			}
		}

		//  Will only happen if unable to read any interaction data.
		if (elements_read == 0) {
			free(data);
			data = NULL;
		} else {
			*size = elements_read;
			*nCores = numprocs;
		}
	}

	return data;
}

// QSORT comparator function.
int small_int2_cmp(const void* p1, const void* p2) {
	const struct small_int2 *a = (const struct small_int2*) p1;
	const struct small_int2 *b = (const struct small_int2*) p2;

	if (a == NULL || b == NULL) return 0;

	if (a->p1 < b->p1) return -1;
	else if (a->p1 > b->p1) return 1;
	else if (a->p1 == b->p1) {
		if (a->p2 < b->p2) return -1;
		else if (a->p2 > b->p2) return 1;
		else if (a->p2 == b->p2) return 0;
	}

	return  0;
}